package zhai.docking.alipay.util;

import java.sql.Timestamp;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
 * @package: zhai.docking.alipay.util
 * @project-name: zhai-docking-alipay
 * @description: 日期工具类
 * @author: Created by 翟凤玺
 * @create-datetime: 2018-9-6 14:11
 */

public class DateUtil {

    public static final String DATETIME_FMT_1 = "yyyyMMddHHmmss";

    public static final String DATETIME_FMT_2 = "yyMMddHHmmss";

    public static final String DATETIME_FMT_3 = "yyyy-MM-dd HH:mm:ss";

    public static final String DATETIME_FMT_4 = "yyyy-MM-dd HH:mm:ss.S";

    public static final String DATE_FMT_1 = "yyyyMMdd";

    public static final String DATE_FMT_2 = "yyMMdd";

    public static final String DATE_FMT_3 = "yyyy-MM-dd";

    public static final String TIME_FMT_1 = "HHmmss";

    public static final String TIME_FMT_2 = "HH:mm:ss";

    /**
     * 比较两个时间毫秒数
     */
    public static long betweenMillis(LocalDateTime start, LocalDateTime end) {
        return ChronoUnit.MILLIS.between(end, start);
    }

    /**
     * localDateTime 转 自定义格式 string
     */
    public static String fmtLocalDateTimeToStr(LocalDateTime localDateTime, String format) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        return localDateTime.format(formatter);
    }

    /**
     * string 转 LocalDateTime
     */
    public static LocalDateTime strToLocalDateTime(String dateStr, String format) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        return LocalDateTime.parse(dateStr, formatter);
    }


    /**
     * 根据时间获取当月有多少天数
     */
    public static int getActualMaximum(Date date) {
        return dateToLocalDateTime(date).getMonth().length(dateToLocalDate(date).isLeapYear());
    }

    /**
     * 根据日期获得星期
     * @return 1:星期一；2:星期二；3:星期三；4:星期四；5:星期五；6:星期六；7:星期日；
     */
    public static int getWeekOfDate(Date date) {
        return dateToLocalDateTime(date).getDayOfWeek().getValue();
    }


    /**
     * 计算两个日期LocalDate相差的天数，不考虑日期前后，返回结果>=0
     */
    public static int getAbsDateDiffDay(LocalDate before, LocalDate after) {
        return Math.abs(Period.between(before, after).getDays());
    }

    /**
     * 计算两个时间LocalDateTime相差的天数，不考虑日期前后，返回结果>=0
     */
    public static int getAbsTimeDiffDay(LocalDateTime before, LocalDateTime after) {
        return Math.abs(Period.between(before.toLocalDate(), after.toLocalDate()).getDays());
    }

    /**
     * 计算两个时间LocalDateTime相差的月数，不考虑日期前后，返回结果>=0
     */
    public static int getAbsTimeDiffMonth(LocalDateTime before, LocalDateTime after) {
        return Math.abs(Period.between(before.toLocalDate(), after.toLocalDate()).getMonths());
    }

    /**
     * 计算两个时间LocalDateTime相差的年数，不考虑日期前后，返回结果>=0
     */
    public static int getAbsTimeDiffYear(LocalDateTime before, LocalDateTime after) {
        return Math.abs(Period.between(before.toLocalDate(), after.toLocalDate()).getYears());
    }

    /**
     * 根据传入日期返回星期几
     * @return 1-7 1：星期天,2:星期一,3:星期二,4:星期三,5:星期四,6:星期五,7:星期六
     */
    public static int getDayOfWeek(Date date) {
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        return cal.get(Calendar.DAY_OF_WEEK);
    }


    /**
     * 获取指定日期的当月的月份数
     */
    public static int getLastMonth(Date date) {
        return dateToLocalDateTime(date).getMonth().getValue();

    }


    /**
     * 特定日期的当月第一天
     */
    public static LocalDate newThisMonth(Date date) {
        LocalDate localDate = dateToLocalDate(date);
        return LocalDate.of(localDate.getYear(), localDate.getMonth(), 1);
    }

    /**
     * 特定日期的当月最后一天
     */
    public static LocalDate lastThisMonth(Date date) {
        int lastDay = getActualMaximum(date);
        LocalDate localDate = dateToLocalDate(date);
        return LocalDate.of(localDate.getYear(), localDate.getMonth(), lastDay);
    }

    /**
     * 特定日期的当年第一天
     */
    public static LocalDate newThisYear(Date date) {
        LocalDate localDate = dateToLocalDate(date);
        return LocalDate.of(localDate.getYear(), 1, 1);

    }

    /**
     * 获取当前时间戳
     */
    public static Timestamp getCurrentTimestamp() {
        return new Timestamp(Instant.now().toEpochMilli());
    }

    /**
     * 获取当前时间
     */
    public static LocalDateTime getCurrentLocalDateTime() {
        return LocalDateTime.now(Clock.system(ZoneId.of("Asia/Shanghai")));
    }

    /**
     * 获取当前时间
     * @return
     */
    public static LocalTime getCurrentLocalTime() {
        return LocalTime.now(Clock.system(ZoneId.of("Asia/Shanghai")));
    }

    /**
     * 修改日期时间的时间部分
     * @param customTime 必须为"hh:mm:ss"这种格式
     */
    public static LocalDateTime reserveDateCustomTime(Date date, String customTime) {
        String dateStr = dateToLocalDate(date).toString() + " " + customTime;
        return strToLocalDateTime(dateStr, "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 修改日期时间的时间部分
     * @param customTime 必须为"hh:mm:ss"这种格式
     */
    public static LocalDateTime reserveDateCustomTime(Timestamp date, String customTime) {
        String dateStr = timestampToLocalDate(date).toString() + " " + customTime;
        return strToLocalDateTime(dateStr, "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 把日期后的时间归0 变成(yyyy-MM-dd 00:00:00:000)
     * @return LocalDateTime
     */
    public static LocalDateTime getZerolizedTime(Date date) {
        LocalDateTime localDateTime = dateToLocalDateTime(date);
        return LocalDateTime.of(localDateTime.getYear(), localDateTime.getMonth(), localDateTime.getDayOfMonth(), 0, 0, 0, 0);

    }

    /**
     * 把日期的时间变成(yyyy-MM-dd 00:00:00:000)
     * @param date
     * @return LocalDateTime
     */
    public static LocalDateTime getZerolizedTime(Timestamp date) {
        LocalDateTime localDateTime = timestampToLocalDateTime(date);
        return LocalDateTime.of(localDateTime.getYear(), localDateTime.getMonth(), localDateTime.getDayOfMonth(), 0, 0, 0, 0);
    }

    /**
     * 把日期的时间变成(yyyy-MM-dd 00:00:00:000)
     * @param date
     * @return LocalDateTime
     */
    public static LocalDateTime getZerolizedTime(LocalDateTime date) {
        return date.withHour(0).withMinute(0).withSecond(0).withNano(0);
    }

    /**
     * 把日期的时间变成(yyyy-MM-dd 23:59:59:999)
     * @param date
     * @return LocalDateTime
     */
    public static LocalDateTime getEndTime(Date date) {
        LocalDateTime localDateTime = dateToLocalDateTime(date);
        return LocalDateTime.of(localDateTime.getYear(), localDateTime.getMonth(), localDateTime.getDayOfMonth(), 23, 59, 59, 999 * 1000000);
    }

    /**
     * 把时间变成(yyyy-MM-dd 23:59:59:999)
     * @param date
     * @return LocalDateTime
     */
    public static LocalDateTime getEndTime(Timestamp date) {
        LocalDateTime localDateTime = timestampToLocalDateTime(date);
        return LocalDateTime.of(localDateTime.getYear(), localDateTime.getMonth(), localDateTime.getDayOfMonth(), 23, 59, 59, 999 * 1000000);
    }

    /**
     * 把时间变成(yyyy-MM-dd 23:59:59:999)
     * @param time
     * @return LocalDateTime
     */
    public static LocalDateTime getEndTime(LocalDateTime time) {
        return time.withHour(23).withMinute(59).withSecond(59).withNano(999999999);
    }

    /**
     * 计算特定时间到 当天 23.59.59.999 的秒数
     *
     * @return
     */
    public static int calculateToEndTime(Date date) {
        LocalDateTime localDateTime = dateToLocalDateTime(date);
        LocalDateTime end = getEndTime(date);
        return (int) (end.toEpochSecond(ZoneOffset.UTC) - localDateTime.toEpochSecond(ZoneOffset.UTC));
    }


    /**
     * 增加或减少年/月/周/天/小时/分/秒数
     *
     * @param localDateTime 例：ChronoUnit.DAYS
     * @param chronoUnit
     * @param num
     * @return LocalDateTime
     */
    public static LocalDateTime addTime(LocalDateTime localDateTime, ChronoUnit chronoUnit, int num) {
        return localDateTime.plus(num, chronoUnit);
    }

    /**
     * 增加或减少年/月/周/天/小时/分/秒数
     *
     * @param chronoUnit 例：ChronoUnit.DAYS
     * @param num
     * @return LocalDateTime
     */
    public static LocalDateTime addTime(Date date, ChronoUnit chronoUnit, int num) {
        long nanoOfSecond = (date.getTime() % 1000) * 1000000;
        LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(date.getTime() / 1000, (int) nanoOfSecond, ZoneOffset.of("+8"));
        return localDateTime.plus(num, chronoUnit);
    }

    /**
     * 增加或减少年/月/周/天/小时/分/秒数
     *
     * @param chronoUnit 例：ChronoUnit.DAYS
     * @param num
     * @return LocalDateTime
     */
    public static LocalDateTime addTime(Timestamp date, ChronoUnit chronoUnit, int num) {
        long nanoOfSecond = (date.getTime() % 1000) * 1000000;
        LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(date.getTime() / 1000, (int) nanoOfSecond, ZoneOffset.of("+8"));
        return localDateTime.plus(num, chronoUnit);
    }

    /**
     * Date 转 LocalDateTime
     *
     * @param date
     * @return LocalDateTime
     */
    public static LocalDateTime dateToLocalDateTime(Date date) {
        long nanoOfSecond = (date.getTime() % 1000) * 1000000;
        LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(date.getTime() / 1000, (int) nanoOfSecond, ZoneOffset.of("+8"));

        return localDateTime;
    }

    /**
     * Timestamp 转 LocalDateTime
     *
     * @param date
     * @return LocalDateTime
     */
    public static LocalDateTime timestampToLocalDateTime(Timestamp date) {
        LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(date.getTime() / 1000, date.getNanos(), ZoneOffset.of("+8"));

        return localDateTime;
    }

    /**
     * Date 转 LocalDate
     *
     * @param date
     * @return LocalDate
     */
    public static LocalDate dateToLocalDate(Date date) {

        return dateToLocalDateTime(date).toLocalDate();
    }

    /**
     * timestamp 转 LocalDateTime
     *
     * @param date
     * @return LocalDate
     */
    public static LocalDate timestampToLocalDate(Timestamp date) {

        return timestampToLocalDateTime(date).toLocalDate();
    }

    /**
     * 比较两个LocalDateTime是否同一天
     *
     * @param begin
     * @param end
     * @return
     */
    public static boolean isTheSameDay(LocalDateTime begin, LocalDateTime end) {
        return begin.toLocalDate().equals(end.toLocalDate());
    }


    /**
     * 比较两个时间LocalDateTime大小
     *
     * @param time1
     * @param time2
     * @return 1:第一个比第二个大；0：第一个与第二个相同；-1：第一个比第二个小
     */
    public static int compareTwoTime(LocalDateTime time1, LocalDateTime time2) {

        if (time1.isAfter(time2)) {
            return 1;
        } else if (time1.isBefore(time2)) {
            return -1;
        } else {
            return 0;
        }
    }

    /**
     * 比较time1,time2两个时间相差的秒数，如果time1<=time2,返回0
     *
     * @param time1
     * @param time2
     */
    public static long getTwoTimeDiffSecond(Timestamp time1, Timestamp time2) {
        long diff = timestampToLocalDateTime(time1).toEpochSecond(ZoneOffset.UTC) - timestampToLocalDateTime(time2).toEpochSecond(ZoneOffset.UTC);
        if (diff > 0) {
            return diff;
        } else {
            return 0;
        }
    }

    /**
     * 比较time1,time2两个时间相差的分钟数，如果time1<=time2,返回0
     *
     * @param time1
     * @param time2
     */
    public static long getTwoTimeDiffMin(Timestamp time1, Timestamp time2) {
        long diff = getTwoTimeDiffSecond(time1, time2) / 60;
        if (diff > 0) {
            return diff;
        } else {
            return 0;
        }
    }

    /**
     * 比较time1,time2两个时间相差的小时数，如果time1<=time2,返回0
     *
     * @param time1
     * @param time2
     */
    public static long getTwoTimeDiffHour(Timestamp time1, Timestamp time2) {
        long diff = getTwoTimeDiffSecond(time1, time2) / 3600;
        if (diff > 0) {
            return diff;
        } else {
            return 0;
        }
    }

    /**
     * 判断当前时间是否在时间范围内
     *
     * @param startTime
     * @param endTime
     * @return
     */
    public static boolean isTimeInRange(Date startTime, Date endTime) throws Exception {
        LocalDateTime now = getCurrentLocalDateTime();
        LocalDateTime start = dateToLocalDateTime(startTime);
        LocalDateTime end = dateToLocalDateTime(endTime);
        return (start.isBefore(now) && end.isAfter(now)) || start.isEqual(now) || end.isEqual(now);
    }

    /**
     * 转 时间戳字符串
     * @param time
     * @return String
     */
    public static String toTimestampStr(LocalDateTime time) {
        return Long.toString(time.toInstant(ZoneOffset.of("+8")).toEpochMilli());
    }

    /**
     * 时间戳字符串 转 特定格式日期字符串
     * @param timeStampStr 时间戳字符串
     * @param formate 格式
     * @return String
     */
    public static String timestampStrToDateStr(String timeStampStr, String formate) {
        DateTimeFormatter ftf = DateTimeFormatter.ofPattern(formate);
        long timeStamp = Long.parseLong(timeStampStr);
        return ftf.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(timeStamp), ZoneId.systemDefault()));
    }

    /**
     * localDateTime 转 LocalTime
     * @param localDateTime
     * @return
     */
    public static LocalTime localDateTimeToLocalTime(LocalDateTime localDateTime) {
        return localDateTime.toLocalTime();
    }

    /**
     * 创建固定时间的localTime
     * @param hour
     * @param minute
     * @param second
     * @return
     */
    public static LocalTime createLocalTime(Integer hour, Integer minute, Integer second) {
        return LocalTime.of(hour, minute, second);
    }

    /**
     * 设置 LocalDateTime 的 时分秒
     * @param dateTime 时间
     * @param hour 时
     * @param minute 分
     * @param second 秒
     * @return
     */
    public static LocalDateTime setLocalDateTime(LocalDateTime dateTime, Integer hour, Integer minute, Integer second) {
        return dateTime.withHour(hour).withMinute(minute).withSecond(second).withNano(0);
    }

    /**
     * 比较时间是否在该时间区间内
     * @param compareTime 需要比较的时间
     * @param startTime 开始时间区间
     * @param endTime 结束时间区间
     * @return
     */
    public static Boolean betweenTime(LocalTime compareTime, LocalTime startTime, LocalTime endTime) {
        int startTimeCompare = compareTime.compareTo(startTime);
        int endTimeCompare = compareTime.compareTo(endTime);
        if (startTimeCompare >= 0 && endTimeCompare <= 0) {
            return true;
        }
        return false;
    }

    /**
     * 比较日期时间是否在该时间区间内
     * @param compareTime 需要比较的时间
     * @param startTime 开始时间区间
     * @param endTime 结束时间区间
     * @return
     */
    public static Boolean betweenDateTime(LocalDateTime compareTime, LocalDateTime startTime, LocalDateTime endTime) {
        int startTimeCompare = compareTime.compareTo(startTime);
        int endTimeCompare = compareTime.compareTo(endTime);
        if (startTimeCompare >= 0 && endTimeCompare <= 0) {
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        Date date = new Date();
        LocalDateTime currentLocalDateTime = DateUtil.getCurrentLocalDateTime();
        Timestamp currentTimestamp = DateUtil.getCurrentTimestamp();
        String currentTimestampStr = DateUtil.toTimestampStr(currentLocalDateTime);
        System.out.println(currentTimestampStr);
        String currentTimeStr = DateUtil.timestampStrToDateStr(currentTimestampStr, DateUtil.DATETIME_FMT_3);
        System.out.println(currentTimeStr);
        LocalDateTime zerolizedTime = DateUtil.getZerolizedTime(date);
        System.out.println(DateUtil.fmtLocalDateTimeToStr(zerolizedTime, DateUtil.DATETIME_FMT_3));
        LocalDateTime zerolizedTime1 = DateUtil.getZerolizedTime(currentLocalDateTime);
        System.out.println(DateUtil.fmtLocalDateTimeToStr(zerolizedTime1, DateUtil.DATETIME_FMT_3));
        LocalDateTime zerolizedTime2 = DateUtil.getZerolizedTime(currentTimestamp);
        System.out.println(DateUtil.fmtLocalDateTimeToStr(zerolizedTime2, DateUtil.DATETIME_FMT_3));

        LocalDateTime endTime = DateUtil.getEndTime(date);
        System.out.println(DateUtil.fmtLocalDateTimeToStr(endTime, DateUtil.DATETIME_FMT_3));
        LocalDateTime endTime1 = DateUtil.getEndTime(currentLocalDateTime);
        System.out.println(DateUtil.fmtLocalDateTimeToStr(endTime1, DateUtil.DATETIME_FMT_3));
        LocalDateTime endTime2 = DateUtil.getEndTime(currentTimestamp);
        System.out.println(DateUtil.fmtLocalDateTimeToStr(endTime2, DateUtil.DATETIME_FMT_3));

        LocalTime localTime = DateUtil.createLocalTime(13, 30, 0);
        LocalTime startBetweenTime = DateUtil.createLocalTime(13, 0, 0);
        LocalTime endBetweenTime = DateUtil.createLocalTime(14, 0, 0);
        System.out.println(DateUtil.betweenTime(localTime, startBetweenTime, endBetweenTime));

        System.out.println(DateUtil.getCurrentLocalTime().toString());

        System.out.println(DateUtil.fmtLocalDateTimeToStr(DateUtil.setLocalDateTime(DateUtil.getCurrentLocalDateTime(), 1,0,0), DateUtil.DATETIME_FMT_3));
        System.out.println(DateUtil.addTime(DateUtil.getCurrentLocalDateTime(), ChronoUnit.HOURS, 2));

        System.out.println(DateUtil.betweenDateTime(DateUtil.getCurrentLocalDateTime(), DateUtil.strToLocalDateTime("2020-11-01 00:00:00", DateUtil.DATETIME_FMT_3), DateUtil.strToLocalDateTime("2020-11-11 00:00:00", DateUtil.DATETIME_FMT_3)));
    }
}
